import { TrackOpTypes, isObject, ProxyArrayWithRaw, RAW } from "../../utils";
import { reactive } from "../../reactive";
import track, { pauseTracking, resumeTracking } from "../../effect/track";

/** 重写数组的方法 */
const arrReconstructionMethods: Record<string, (...args: any[]) => any> = {};

["includes", "indexOf", "lastIndexOf"].forEach((methodName) => {
  arrReconstructionMethods[methodName] = function (
    /** ts的类型注解，并非实际形参 */
    this: ProxyArrayWithRaw,
    ...args: any[]
  ) {
    const method = (
      Array.prototype as unknown as { [key: string]: (...args: any[]) => any }
    )[methodName];

    // 从代理对象中去执行这三个方法：基本数据类型能够被找到，引用数据类型是找不到的
    const resultOnProxy = method.apply(this, args); // 此时的this指向的是代理对象

    // indexOf和lastIndexOf是-1 或者 inclues是false
    if (resultOnProxy === -1 || resultOnProxy === false) {
      // 去原始对象上寻找（this[RAW]是原始对象）
      return method.apply(this[RAW], args);
    }
    return resultOnProxy;
  };
});

["push", "pop", "shift", "unshift", "splice"].forEach((methodName) => {
  arrReconstructionMethods[methodName] = function (
    /** ts的类型注解，并非实际形参 */
    this: ProxyArrayWithRaw,
    ...args: any[]
  ) {
    // 1.先暂停收集依赖
    pauseTracking();

    // 2.执行本身的方法
    const method = (
      Array.prototype as unknown as { [key: string]: (...args: any[]) => any }
    )[methodName];
    const resultOnProxy = method.apply(this, args);

    // 3.恢复依赖
    resumeTracking();
    return resultOnProxy;
  };
});

const getHandler: ProxyHandler<object>["get"] = (target, key) => {
  // 在执行includes、indexOf、lastIndexOf重写的方法时，
  // 在代理对象上找不到时，会执行this[RAW]，此时会进入到getHandler，
  // 此时key就是RAW，返回原始对象，因此this[RAW]返回的就是原始对象
  if (key === RAW) return target;

  // 依赖收集
  track(target, TrackOpTypes.GET, key);

  const result = Reflect.get(target, key);

  // 对数组的includes、indexOf、lastIndexOf方法进行重写，保证其在查询引用类型时，能够正确找到
  if (arrReconstructionMethods.hasOwnProperty(key) && Array.isArray(target)) {
    return arrReconstructionMethods[key as string];
  }

  // 获取到的成员可能是对象，需要递归处理，将其转换为响应式
  if (isObject(result)) {
    return reactive(result);
  }

  return result;
};

export default getHandler;
